package tcode

import (
	"context"
	"errors"
	"fmt"
	"os"
	"os/exec"
	"strings"
	"text/template"
)

type dataTypeFun func(filedInfo *fieldInfo) error

type CodeFactory interface {
	currentDbName(ctx context.Context) (string, error)
	columnTypeParse(filedInfo *fieldInfo) error
	parseFields(ctx context.Context, fullyTableName string) ([]*fieldInfo, error)
	selectTableName(ctx context.Context) ([]string, error)
	ToStructInfo(ctx context.Context, fullyTableName, tableComment string) (*structInfo, error)
	ToAllStructInfo(ctx context.Context) ([]*structInfo, error)
	ToAllStructInfoOtherDb(ctx context.Context, dbName string) ([]*structInfo, error)
}

type fieldInfo struct {
	ColumnName    string   //列名
	Name          string   //属性名称
	JsonName      string   //json名称
	ColumnType    string   //数据列类型
	Type          string   //属性类型
	Collection    string   //字符集
	Length        int      //长度
	Comment       string   //注释
	NotNull       bool     //非空
	AutoIncrement bool     //是否自增
	PrimaryKey    bool     //是否主键
	DefaultValue  string   //默认值
	IsEnum        bool     //是否枚举类型
	IsSet         bool     //是否set类型
	Member        []string //枚举或set集合的成员
	Accuracy      int      //浮点数据类型精度
	ImportPackage string
}

type structInfo struct {
	DbName         string
	TableName      string
	FileName       string
	Name           string
	Comment        string
	Fields         []*fieldInfo
	ImportPackages []string
	PackageName    string
}

func (receiver *structInfo) WriteDir() string {
	return receiver.DbName + "/" + receiver.PackageName + "/"
}

func (receiver *structInfo) FullFileName() string {
	return receiver.WriteDir() + receiver.FileName
}

// WriteStruct 将*StructInfo信息通过模板codeTemplateText解析导出至defaultAbsDir路径
func WriteStruct(ctx context.Context, info *structInfo) error {
	return WriteStructTo(ctx, info, defaultAbsDir)
}

// WriteStructTo 将*StructInfo信息通过模板codeTemplateText解析导出至absDir路径
func WriteStructTo(ctx context.Context, info *structInfo, absDir string) error {
	if info == nil {
		err := errors.New("info is nil")
		FuncLog(err)
		return err
	}
	//检查是否包含有主键列
	fields := info.Fields
	pkName := GetPkColumnName(ctx)
	checkHasPk := false
	for i := range fields {
		if fields[i].ColumnName == pkName {
			checkHasPk = true
			break
		}
	}
	if !checkHasPk {
		err := errors.New(fmt.Sprint("warn: please confirm table ", info.TableName, " include the primary key column: ", pkName))
		FuncLog(err)
		return err
	}
	if len(absDir) <= 0 {
		absDir = defaultAbsDir
	}
	if !strings.HasSuffix(absDir, "/") {
		absDir += "/"
	}
	if _, err := os.Stat(absDir + info.WriteDir()); os.IsNotExist(err) {
		err = os.MkdirAll(absDir+info.WriteDir(), 0666)
		if err != nil {
			FuncLog(err)
			return err
		}
	}
	codeTemplate, err := template.New("codeTemplate").Parse(codeTemplateText)
	if err != nil {
		FuncLog(err)
		return err
	}
	file, err := os.Create(absDir + info.FullFileName())
	if err != nil {
		FuncLog(err)
		return err
	}
	defer func(file *os.File) {
		err = file.Close()
		if err != nil {
			FuncLog(err)
		}
	}(file)

	err = codeTemplate.Execute(file, info)
	if err != nil {
		FuncLog(err)
		return err
	}
	go func() {
		cmd := exec.Command("gofmt", "-w", absDir+info.FullFileName())
		err = cmd.Run()
		if err != nil {
			FuncLog("gofmt err", err)
		}
	}()
	FuncLog("table", info.TableName, "writer to", absDir+info.FullFileName(), "success")
	return nil
}
